package cn.jbolt.core.service.base;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.jbolt.core.base.JBoltIDGenMode;
import cn.jbolt.core.base.JBoltMsg;
import cn.jbolt.core.base.TreeCheckIsParentNode;
import cn.jbolt.core.base.config.JBoltConfig;
import cn.jbolt.core.bean.JsTreeBean;
import cn.jbolt.core.bean.Option;
import cn.jbolt.core.bean.OptionBean;
import cn.jbolt.core.consts.JBoltConst;
import cn.jbolt.core.db.datasource.JBoltDataSourceUtil;
import cn.jbolt.core.db.datasource.JBoltDatasource;
import cn.jbolt.core.db.sql.DBType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.db.util.JBoltDbKit;
import cn.jbolt.core.kit.JBoltSnowflakeKit;
import cn.jbolt.core.para.jbolttablemenufilter.JBoltTableMenuFilter;
import cn.jbolt.core.util.JBoltArrayUtil;
import cn.jbolt.core.util.JBoltCamelCaseUtil;
import cn.jbolt.core.util.JBoltListMap;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Okv;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.*;
import com.jfinal.plugin.activerecord.Record;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * 采用Db+record模式实现 单表版
 *
 * @ClassName: JBoltCommonRecordService
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2019年7月20日
 */
public abstract class JBoltBaseRecordService extends JBoltCommonService {
	/**
	 * 获取表名
	 * @return
	 */
	protected String table(){
		if(notOk(tableName)){
			tableName = getTableName();
		}
		return tableName;
	}

	/**
	 * 获取record指定的表名
	 * @return
	 */
	protected abstract String getTableName();

	/**
	 * 获取序列名 默认就是表名
	 *
	 * @return
	 */
	public String idSequence() {
		return table();
	}

	/**
	 * 获取数据库名称 没有返回null
	 * @return
	 */
	protected String database(){
		if(notOk(databaseName)){
			JBoltDatasource datasource = JBoltDataSourceUtil.me.getJBoltDatasource(dataSourceConfigName());
			if(datasource != null){
				databaseName = datasource.getDbName();
			}
		}
		return databaseName;
	}

	@Override
	protected String schema() {
		if(notOk(schema)){
			JBoltDatasource datasource = JBoltDataSourceUtil.me.getJBoltDatasource(dataSourceConfigName());
			if(datasource != null){
				schema = datasource.getDbSchema();
			}
		}
		return schema;
	}

	/**
	 * 获取数据库名称 没有返回null
	 * @return
	 */
	protected String dataSourceConfigName(){
		if(notOk(dataSourceConfigName)){
			dataSourceConfigName = getDataSourceConfigName();
			if(notOk(dataSourceConfigName)){
				dataSourceConfigName = DbKit.MAIN_CONFIG_NAME;
			}
		}
		return dataSourceConfigName;
	}

	/**
	 * 获取record指定表所属数据源配置名称
	 * @return
	 */
	protected abstract String getDataSourceConfigName();

	/**
	 * 获取数据库表主键名 默认id
	 * @return
	 */
	protected String primaryKey(){
		if(notOk(primaryKey)){
			primaryKey = getPrimaryKey();
			if(notOk(primaryKey)){
				primaryKey = ID;
			}
		}
		return primaryKey;
	}

	/**
	 * 获取自定义主键名称 多个用逗号隔开
	 * @return
	 */
	protected abstract String getPrimaryKey();

	/**
	 * 获取数据库类型
	 *
	 * @return
	 */
	protected String dbType(){
		if(notOk(dbType)){
			JBoltDatasource datasource = JBoltDataSourceUtil.me.getJBoltDatasource(dataSourceConfigName());
			if(datasource != null){
				dbType = datasource.getDbType();
			}
		}
		return dbType;
	}

	/**
	 * 获取数据库主键策略 默认snowflake
	 *
	 * @return
	 */
	protected String idGenMode(){
		if(notOk(idGenMode)){
			idGenMode = getIdGenMode();
			if(notOk(idGenMode)){
				JBoltDatasource datasource = JBoltDataSourceUtil.me.getJBoltDatasource(dataSourceConfigName());
				if(datasource == null){
					idGenMode = JBoltIDGenMode.SNOWFLAKE;
				}else{
					idGenMode = datasource.getIdGenMode();
				}
			}
		}
		return idGenMode;
	}

	/**
	 * 自定义主键策略 如果null 则使用数据源配置的默认值
	 * @return
	 */
	protected abstract String getIdGenMode();

	/**
	 * 获取Sql模板 Db模式
	 *
	 * @param key
	 * @return
	 */
	public DbTemplate dbTemplate(String key) {
		return dbTemplate(key, Okv.create());
	}

	/**
	 * 获取Sql模板 Db模式
	 *
	 * @param key
	 * @param data
	 * @return
	 */
	public DbTemplate dbTemplate(String key, Kv data) {
		return dbTemplate(key, Okv.create().set(data));
	}

	/**
	 * 获取Sql模板 Db模式
	 *
	 * @param key
	 * @param data
	 * @return
	 */
	public DbTemplate dbTemplate(String key, Okv data) {
		String tableName = table();
		if(DBType.isDM(dbType())){
			String database = database();
			if(isOk(database)){
				tableName = Sql.KEY_QUOTATION_MARKS + database + Sql.KEY_QUOTATION_MARKS + Sql.KEY_DOT + Sql.KEY_QUOTATION_MARKS + tableName + Sql.KEY_QUOTATION_MARKS;
			}else{
				tableName = Sql.KEY_QUOTATION_MARKS + tableName + Sql.KEY_QUOTATION_MARKS;
			}
		}
		data.setIfNotBlank(SQL_TPL_KEY_TABLE, tableName);
		data.setIfNotBlank(SQL_TPL_KEY_PRIMARYKEY, primaryKey());
		return Db.use(dataSourceConfigName()).template(key, data);
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn
	 * @param valueColumn
	 * @param paras
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn, Okv paras) {
		return getOptionList(textColumn, valueColumn, paras, null, null);
	}

	/**
	 * 按照指定字段和排序方式返回options
	 *
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<Option> getOptionListOrderBy(String orderColumns, String orderTypes) {
		return getOptionList(NAME, ID, null, orderColumns, orderTypes);
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn   哪个字段作为显示的text
	 * @param valueColumn  哪个字段作为选中的值
	 * @param paras        查询条件
	 * @param orderColumns 排序字段 可多个
	 * @param orderTypes   排序方式 可多个
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn, Okv paras, String orderColumns,
			String orderTypes) {
		Okv conf = Okv.by(VALUE, valueColumn).set(TEXT, textColumn).set(SQL_TPL_KEY_MYPARAS, paras)
				.set(SQL_TPL_KEY_CUSTOM_COMPARE, false);
		if (isOk(orderColumns)) {
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, JBoltArrayUtil.from(orderColumns, KEY_COMMA));
		}
		if (isOk(orderTypes)) {
			conf.set(SQL_TPL_KEY_ORDER_TYPES, JBoltArrayUtil.from(orderTypes, KEY_COMMA));
		}
		List<Record> records = dbTemplate(SQL_TPL_COMMON_OPTIONLIST, conf).find();
		return toOptions(records);
	}

	/**
	 * 转为options
	 *
	 * @param records
	 * @return
	 */
	private List<Option> toOptions(List<Record> records) {
		List<Option> options = new ArrayList<Option>();
		records.forEach(record -> options.add(new OptionBean(record.get("text"), record.get("value"))));
		return options;
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @param textColumn  哪个字段作为显示的text
	 * @param valueColumn 哪个字段作为选中的值
	 * @return
	 */
	public List<Option> getOptionList(String textColumn, String valueColumn) {
		return getOptionList(textColumn, valueColumn, null);
	}

	/**
	 * 得到下拉列表数据
	 *
	 * @return
	 */
	public List<Option> getOptionList() {
		return getOptionList(NAME, ID);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 自定义参数compare
	 *
	 * @param columns
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public List<Record> getCommonList(String columns, Okv paras, boolean customCompare) {
		return dbTemplate(SQL_TPL_COMMON_LIST, Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare)
				.setIfNotNull(SQL_TPL_KEY_MYPARAS, paras).setIfNotBlank(SQL_TPL_KEY_COLUMNS, columns)).find();
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 可以自定义参数compare 默认=
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public List<Record> getCommonList(Okv paras, boolean customCompare) {
		return getCommonList(Sql.KEY_STAR, paras, customCompare);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 不能自定义参数compare 默认=
	 *
	 * @param paras
	 * @return
	 */
	public List<Record> getCommonList(Okv paras) {
		return getCommonList(Sql.KEY_STAR, paras, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 不能排序 不能自定义参数compare 默认=
	 *
	 * @param columns 指定查询的列
	 * @param paras
	 * @return
	 */
	public List<Record> getCommonList(String columns, Okv paras) {
		return getCommonList(columns, paras, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 默认正序 不能自定义参数compare 默认=
	 *
	 * @param paras       查询条件
	 * @param orderColums 排序列
	 * @return
	 */
	public List<Record> getCommonList(Okv paras, String orderColums) {
		if (notOk(orderColums)) {
			return getCommonList(paras);
		}
		int count = StrUtil.count(orderColums, KEY_COMMA);
		String orderTypes = "";
		for (int i = 0; i <= count; i++) {
			if (i == 0) {
				orderTypes = Sql.KEY_ASC_TRIMED;
			} else {
				orderTypes = orderTypes + "," + Sql.KEY_ASC_TRIMED;
			}
		}
		return getCommonList(Sql.KEY_STAR, paras, orderColums, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<Record> getCommonList(Okv paras, String orderColumns, String orderTypes) {
		return getCommonList(Sql.KEY_STAR, paras, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<Record> getCommonList(String columns, Okv paras, String orderColumns, String orderTypes) {
		return getCommonList(columns, paras, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 不能自定义参数compare 默认=
	 *
	 * @param columns
	 * @param orderColumns
	 * @param orderTypes
	 * @return
	 */
	public List<Record> getCommonList(String columns, String orderColumns, String orderTypes) {
		return getCommonList(columns, null, orderColumns, orderTypes, false);
	}

	/**
	 * 常用的得到列表数据的方法 不分页版 可以排序 自定义参数compare
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param customCompare
	 * @return
	 */
	public List<Record> getCommonList(String columns, Okv paras, String orderColumns, String orderTypes,
			boolean customCompare) {
		Okv conf = Okv.by(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare);
		if (isOk(columns)) {
			conf.set("columns", columns);
		}
		if (isOk(orderColumns)) {
			conf.set("orderColumns", JBoltArrayUtil.from(orderColumns, ","));
		}
		if (isOk(orderTypes)) {
			conf.set("orderTypes", JBoltArrayUtil.from(orderTypes, ","));
		}
		return dbTemplate("common.list", conf).find();
	}

	/**
	 * 分页查询
	 *
	 * @param sql
	 * @param columnToCamelCase 字段名转驼峰
	 * @return
	 */
	public Page<Record> paginate(Sql sql, boolean columnToCamelCase) {
		Sql totalCountSql = ObjectUtil.clone(sql);
		if(sql.isQueryDistinct()){
			totalCountSql.distinctToDistinctCount();
		}else{
			totalCountSql.count();
		}
		return processPaginate(sql, totalCountSql, columnToCamelCase);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 自定义参数compare 分页查询
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @param or
	 * @return
	 */
	public Page<Record> paginate(String columns, Okv paras, String orderColumns, String orderTypes, int pageNumber,
			int pageSize, boolean customCompare, boolean or) {
		Okv conf = Okv.by(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare);
		conf.set("or", or);
		conf.setIfNotBlank("columns", columns);
		if (isOk(orderColumns)) {
			conf.set("orderColumns", JBoltArrayUtil.from(orderColumns, ","));
		}
		if (isOk(orderTypes)) {
			conf.set("orderTypes", JBoltArrayUtil.from(orderTypes, ","));
		}
		return dbTemplate("common.list", conf).paginate(pageNumber, pageSize);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param columns
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(String columns, Okv paras, String orderColumns, String orderTypes, int pageNumber,
			int pageSize) {
		return paginate(columns, paras, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @param or
	 * @return
	 */
	public Page<Record> paginate(Okv paras, String orderColumns, String orderTypes, int pageNumber, int pageSize,
			boolean customCompare, boolean or) {
		return paginate(Sql.KEY_STAR, paras, orderColumns, orderTypes, pageNumber, pageSize, customCompare, or);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<Record> paginate(Okv paras, String orderColumns, String orderTypes, int pageNumber, int pageSize,
			boolean customCompare) {
		return paginate(Sql.KEY_STAR, paras, orderColumns, orderTypes, pageNumber, pageSize, customCompare, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param paras
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(Okv paras, String orderColumns, String orderTypes, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, paras, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 可以排序 条件都是等于 分页查询
	 *
	 * @param orderColumns
	 * @param orderTypes
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(String orderColumns, String orderTypes, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, null, orderColumns, orderTypes, pageNumber, pageSize, false, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 不尅一可以排序 条件自定义 customCompare 分页查询
	 *
	 * @param columns
	 * @param paras
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<Record> paginate(String columns, Okv paras, int pageNumber, int pageSize, boolean customCompare) {
		Okv conf = Okv.by(SQL_TPL_KEY_MYPARAS, paras).set(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare);
		if (isOk(columns)) {
			conf.set("columns", columns);
		}
		return dbTemplate("common.list", conf).paginate(pageNumber, pageSize);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 条件自定义 customCompare 分页查询
	 *
	 * @param paras
	 * @param pageNumber
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<Record> paginate(Okv paras, int pageNumber, int pageSize, boolean customCompare) {
		return paginate(Sql.KEY_STAR, paras, pageNumber, pageSize, customCompare);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param paras
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(Okv paras, int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, paras, pageNumber, pageSize, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param paras
	 * @param pageSize
	 * @param customCompare
	 * @return
	 */
	public Page<Record> paginate(Okv paras, int pageSize, boolean customCompare) {
		return paginate(Sql.KEY_STAR, paras, 1, pageSize, customCompare);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(int pageNumber, int pageSize) {
		return paginate(Sql.KEY_STAR, null, pageNumber, pageSize, false);
	}

	/**
	 * 按照sql模板分页查询
	 *
	 * @param key
	 * @param data
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginateBySqlTemplate(String key, Okv data, int pageNumber, int pageSize) {
		return dbTemplate(key, data).paginate(pageNumber, pageSize);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param paras
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(Okv paras, int pageSize) {
		return paginate(Sql.KEY_STAR, paras, 1, pageSize, false);
	}

	/**
	 * 常用的得到分页列表数据的方法 不可以排序 分页查询
	 *
	 * @param pageSize
	 * @return
	 */
	public Page<Record> paginate(int pageSize) {
		return paginate(Sql.KEY_STAR, null, 1, pageSize, false);
	}

	/**
	 * 根据ID 批量删除 指定分隔符版
	 *
	 * @param ids
	 * @param split 分隔符
	 * @return
	 */
	public Ret deleteByIds(String ids, String split, boolean checkCanDelete) {
		return deleteByIds(JBoltArrayUtil.from3(ids, split), checkCanDelete);
	}

	/**
	 * 根据ID 批量删除 指定分隔符版
	 *
	 * @param ids
	 * @param split 分隔符
	 * @return
	 */
	public Ret deleteByIds(String ids, String split) {
		return deleteByIds(ids, split, false);
	}

	/**
	 * 根据ID 批量删除 默认分隔符是逗号
	 *
	 * @param ids
	 * @return
	 */
	public Ret deleteByIds(String ids) {
		return deleteByIds(ids, false);
	}

	/**
	 * 根据ID 批量删除 默认分隔符是逗号
	 *
	 * @param ids
	 * @param checkCanDelete
	 * @return
	 */
	public Ret deleteByIds(String ids, boolean checkCanDelete) {
		return deleteByIds(ids, ",", checkCanDelete);
	}

	/**
	 * 根据ID 批量删除
	 *
	 * @param ids
	 * @return
	 */
	public Ret deleteByIds(Object[] ids) {
		return deleteByIds(ids, false);
	}

	/**
	 * 根据ID 批量删除
	 *
	 * @param ids
	 * @param checkCanDelete
	 * @return
	 */
	public Ret deleteByIds(Object[] ids, boolean checkCanDelete) {
		if (notOk(ids)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		Ret ret;
		for (Object id : ids) {
			ret = deleteById(id, checkCanDelete);
			if (ret.isFail()) {
				return ret;
			}
		}
		return SUCCESS;
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @return
	 */
	public Ret deleteById(Object id) {
		return deleteById(id, null);
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @param kv 额外参数
	 * @return
	 */
	public Ret deleteById(Object id, Kv kv) {
		return deleteById(id, false, kv);
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @param checkCanDelete
	 * @return
	 */
	public Ret deleteById(Object id, boolean checkCanDelete) {
		return deleteById(id, checkCanDelete, null);
	}

	/**
	 * 通用根据ID删除数据
	 *
	 * @param id
	 * @param checkCanDelete
	 * @param kv             额外参数
	 * @return
	 */
	public Ret deleteById(Object id, boolean checkCanDelete, Kv kv) {
		return deleteById(id, false, checkCanDelete, kv);
	}

	/**
	 * 通用根据ID删除数据 需要先检查是否被其他地方使用
	 *
	 * @param id
	 * @param checkInUse
	 * @param returnDeleteData
	 * @param kv
	 * @return
	 */
	public Ret deleteById(Object id, boolean checkInUse, boolean returnDeleteData, Kv kv) {
		if (JBoltConfig.DEMO_MODE) {
			return fail(JBoltMsg.DEMO_MODE_CAN_NOT_DELETE);
		}
		if (notOk(id)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		String tableName = table();
		Record record = Db.use(dataSourceConfigName()).findById(tableName, id);
		if (record == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}
		if (checkInUse) {
			String msg = checkCanDelete(record, kv);
			if (StrKit.notBlank(msg)) {
				return fail(msg);
			}
		}

		boolean success = Db.use(dataSourceConfigName()).delete(tableName, record);
		if (success) {
			String msg = afterDelete(record, kv);
			if (isOk(msg)) {
				return fail(msg);
			}
		}
		// 需要返回删除的数据
		if (returnDeleteData) {
			return success ? success(record, JBoltMsg.SUCCESS) : fail();
		}
		return ret(success);
	}


	/**
	 * 根据ID追回假删数据 不检测是否可以恢复
	 *
	 * @param id
	 * @return
	 */
	public Ret recoverById(Object id) {
		return recoverById(id,false);
	}

	/**
	 * 根据ID追回假删数据
	 *
	 * @param id
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverById(Object id, boolean checkCanRecover) {
		if (JBoltConfig.DEMO_MODE) {
			return fail(JBoltMsg.DEMO_MODE_CAN_NOT_DELETE);
		}
		if (notOk(id)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		String tableName = table();
		Record record = Db.use(dataSourceConfigName()).findById(tableName, id);
		if (record == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}

		boolean success = false;
		if (record.getColumns().containsKey("is_deleted")) {
			if (checkCanRecover) {
				String msg = checkCanRecover(record);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
			// 如果存在 假删控制字段 就执行假删除
			record.set("is_deleted", FALSE);
			success = Db.use(dataSourceConfigName()).update(tableName, record);
			if (success) {
				String msg = afterRecover(record);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
		} else if (record.getColumns().containsKey("IS_DELETED")) {
			if (checkCanRecover) {
				String msg = checkCanRecover(record);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
			// 如果存在 假删控制字段 就执行假删除
			record.set("IS_DELETED", FALSE);
			success = Db.use(dataSourceConfigName()).update(tableName, record);
			if (success) {
				String msg = afterRecover(record);
				if (isOk(msg)) {
					return fail(msg);
				}
			}
		} else {
			return fail("此表数据无法执行追回操作，表内不包含is_deleted字段");
		}

		return ret(success);
	}

	/**
	 * 删除成功后需要处理的事情
	 *
	 * @param record
	 * @param kv
	 * @return
	 */
	protected String afterDelete(Record record, Kv kv) {
		return null;
	}

	/**
	 * 恢复成功后需要处理的事情
	 *
	 * @param record
	 * @return
	 */
	protected String afterRecover(Record record) {
		return null;
	}

	/**
	 * 额外需要处理toggle操作
	 *
	 * @param record
	 * @param column
	 * @param kv
	 * @return
	 */
	protected String afterToggleBoolean(Record record, String column, Kv kv) {
		return null;
	}

	/**
	 * 检测数据是否可以被删除 带额外数据
	 *
	 * @param record
	 * @param kv
	 * @return
	 */
	public String checkCanDelete(Record record, Kv kv) {
		return null;
	}

	/**
	 * 检测数据是否可以被恢复
	 *
	 * @param record
	 * @return
	 */
	public String checkCanRecover(Record record) {
		return null;
	}

	/**
	 * 检测数据是否被其它数据外键引用
	 *
	 * @param record
	 * @param kv     额外参数
	 * @return
	 */
	public String checkInUse(Record record, Kv kv) {
		return null;
	}

	/**
	 * 检测数据是否字段是否可以执行切换true false
	 *
	 * @param record
	 * @param column
	 * @param kv
	 * @return
	 */
	public String checkCanToggle(Record record, String column, Kv kv) {
		return null;
	}

	/**
	 * 判断name是否存在相同数据 排除指定ID
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean existsName(String nameValue, Object excludedIdValue) {
		return existsName(nameValue, excludedIdValue, null);
	}

	/**
	 * 判断sn是否存在相同数据 排除指定ID
	 *
	 * @param snValue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean existsSn(String snValue, Object excludedIdValue) {
		return existsSn(snValue, excludedIdValue, null);
	}

	/**
	 * 判断name是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsName(String nameValue, Object excludedIdValue, Object pidValue) {
		return existsName(nameValue, excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断sn是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param snValue
	 * @param excludedIdValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsSn(String snValue, Object excludedIdValue, Object pidValue) {
		return existsSn(snValue, excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断name是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param nameValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsName(String nameValue, Object excludedIdValue, String pidName, Object pidValue) {
		return exists(NAME, nameValue, excludedIdValue, pidName, pidValue);
	}

	/**
	 * 判断sn是否存在相同数据 排除指定ID 可以指定在哪一个PID下
	 *
	 * @param snValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsSn(String snValue, Object excludedIdValue, String pidName, Object pidValue) {
		return exists(SN, snValue, excludedIdValue, pidName, pidValue);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnValue
	 * @param excludedIdValue
	 * @param pidValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnValue, Object excludedIdValue, Object pidValue) {
		return exists(columnName, columnValue, excludedIdValue, PID, pidValue);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnValue
	 * @param excludedIdValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnValue, Object excludedIdValue, String pidName,
			Object pidValue) {
		Sql sql = selectSql().select(primaryKey()).eqQM(columnName).idNotEqQM(primaryKey()).first();
		Object existId = null;
		if (StrKit.notBlank(pidName) && pidValue != null) {
			sql.eqQM(pidName);
			existId = queryColumn(sql, columnValue, excludedIdValue, pidValue);
		} else {
			existId = queryColumn(sql, columnValue, excludedIdValue);
		}
		return isOk(existId);
	}

	/**
	 * 判断是否存在满足条件的数据
	 *
	 * @param sql
	 * @return
	 */
	public boolean exists(Sql sql) {
		sql.select(primaryKey()).first();
		Object existId = queryColumn(sql);
		return isOk(existId);
	}

	/**
	 * 判断是否存在满足条件的数据
	 *
	 * @param sql
	 * @param whereValues
	 * @return
	 */
	public boolean exists(Sql sql, Object... whereValues) {
		sql.select(primaryKey()).first();
		Object existId = queryColumn(sql, whereValues);
		return isOk(existId);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnvalue
	 * @param excludedIdValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnvalue, Object excludedIdValue) {
		return exists(columnName, columnvalue, excludedIdValue, null, null);
	}

	/**
	 * 判断在子节点里是否存在指定列值数据
	 *
	 * @param columnName
	 * @param columnvalue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsInSons(String columnName, Object columnvalue, String pidName, Object pidValue) {
		return exists(columnName, columnvalue, -1, pidName, pidValue);
	}

	/**
	 * 判断数据库是否存在指定id的值
	 *
	 * @param idValue
	 * @return
	 */
	public boolean existsById(Object idValue) {
		if (notOk(idValue)) {
			//如果传值有问题
			throw new RuntimeException(JBoltMsg.PARAM_ERROR);
		}
		Sql sql = selectSql().selectId().idEqQM(primaryKey()).first();
		Object existId = queryColumn(sql, idValue);
		return isOk(existId);
	}

	/**
	 * 判断是否存在子节点
	 *
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsSon(String pidName, Object pidValue) {
		if (notOk(pidName) || notOk(pidValue)) {
			// 如果传值有问题
			throw new RuntimeException(JBoltMsg.PARAM_ERROR);
		}
		Sql sql = selectSql().selectId().eqQM(pidName).first();
		Object existId = queryColumn(sql, pidValue);
		return isOk(existId);
	}

	/**
	 * 判断是否存在子节点
	 *
	 * @param pidValue
	 * @return
	 */
	public boolean existsSon(Object pidValue) {
		return existsSon(PID, pidValue);
	}

	/**
	 * 判断是否存在
	 *
	 * @param columnName
	 * @param columnValue
	 * @return
	 */
	public boolean exists(String columnName, Object columnValue) {
		return exists(columnName, columnValue, -1);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @return
	 */
	public boolean existsName(String nameValue) {
		return existsName(nameValue, -1);
	}

	/**
	 * 判断sn值为指定值的数据是否存在
	 *
	 * @param snValue
	 * @return
	 */
	public boolean existsSn(String snValue) {
		return existsSn(snValue, -1);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @return
	 */
	public boolean existsNameWithPid(String nameValue, String pidName, Object pidValue) {
		return existsName(nameValue, -1, pidName, pidValue);
	}

	/**
	 * 判断sn值为指定值的数据是否存在
	 * @param snValue
	 * @param pidName
	 * @param pidValue
	 * @return
	 */
	public boolean existsSnWithPid(String snValue, String pidName, Object pidValue) {
		return existsSn(snValue, -1, pidName, pidValue);
	}

	/**
	 * 判断name值为指定值的数据是否存在
	 *
	 * @param nameValue
	 * @return
	 */
	public boolean existsNameWithPid(String nameValue, Object pidValue) {
		return existsNameWithPid(nameValue, PID, pidValue);
	}

	/**
	 * 判断sn值为指定值的数据是否存在
	 *
	 * @param snValue
	 * @param pidValue
	 * @return
	 */
	public boolean existsSnWithPid(String snValue, Object pidValue) {
		return existsSnWithPid(snValue, PID, pidValue);
	}

	/**
	 * 多主键表根据多主键值获取一个Record
	 * @return
	 */
	public Record findByIds(Object... idValues){
		return Db.use(dataSourceConfigName()).findByIds(table(),primaryKey(),idValues);
	}

	/**
	 * 根据多个IDS拼接的字符串 获得一组指定主键ID的数据
	 * @param idsStr
	 * @return
	 */
	public List<Record> getListByIds(String idsStr) {
		if (notOk(idsStr)) {
			return null;
		}
		return getListByIds(JBoltArrayUtil.from3(idsStr,KEY_COMMA));
	}
	/**
	 * 根据多个IDS 获得一组指定主键ID的数据
	 *
	 * @param ids
	 * @return
	 */
	public List<Record> getListByIds(Object[] ids) {
		if (notOk(ids)) {
			return null;
		}
		String primaryKey = primaryKey();
		return find(selectSql().in(primaryKey,ids).asc(primaryKey));
	}

	/**
	 * 根据ID获得一条数据
	 *
	 * @param id
	 * @return
	 */
	public Record findById(Object id) {
		if (notOk(id)) {
			return null;
		}
		return Db.use(dataSourceConfigName()).findById(table(), id);
	}

	/**
	 * 根据ID获得一条数据
	 *
	 * @param id
	 * @param columnToCamelCase
	 * @return
	 */
	public Record findById(Object id, boolean columnToCamelCase) {
		if (notOk(id)) {
			return null;
		}
		Record record = Db.use(dataSourceConfigName()).findById(table(), id);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(record);
		}
		return record;
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @return
	 */
	public Record findFirst(Okv paras) {
		return findFirst(paras, false);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @param columnToCamelCase
	 * @return
	 */
	public Record findFirst(Okv paras, boolean columnToCamelCase) {
		return findFirst(paras, false, columnToCamelCase);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @return
	 */
	public Record findFirst() {
		return findFirst(false);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param columnToCamelCase
	 * @return
	 */
	public Record findFirst(boolean columnToCamelCase) {
		return findFirst(null, false, columnToCamelCase);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param customCompare
	 * @param columnToCamelCase
	 * @return
	 */
	public Record findFirst(boolean customCompare, boolean columnToCamelCase) {
		return findFirst(null, customCompare, columnToCamelCase);
	}

	/**
	 * 得到符合条件的第一个
	 *
	 * @param paras
	 * @param customCompare
	 * @param columnToCamelCase
	 * @return
	 */
	public Record findFirst(Okv paras, boolean customCompare, boolean columnToCamelCase) {
		Record record = dbTemplate("common.first",
				Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras)).findFirst();
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(record);
		}
		return record;
	}

	/**
	 * 随机得到符合条件的第一个
	 *
	 * @param paras
	 * @return
	 */
	public Record getRandomOne(Okv paras) {
		return getRandomOne(paras, false);
	}

	/**
	 * 随机得到符合条件的第一个
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public Record getRandomOne(Okv paras, boolean customCompare) {
		return getRandomOne(paras, customCompare, false);
	}

	/**
	 * 随机得到符合条件的第一个
	 *
	 * @param paras
	 * @param customCompare
	 * @param columnToCamelCase
	 * @return
	 */
	public Record getRandomOne(Okv paras, boolean customCompare, boolean columnToCamelCase) {
//		if(JBoltConfig.isOracle()) {
//			return getOracleRandomOne(paras,customCompare);
//		}
		Okv conf = Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras);
		switch (dbType()) {
		case DBType.MYSQL:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_MYSQL });
			break;
		case DBType.POSTGRESQL:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_POSTGRESQL });
			break;
		case DBType.SQLSERVER:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_NEWID_SQLSERVER });
			break;
		case DBType.DM:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_DM });
			break;
		case DBType.ORACLE:
			conf.set(SQL_TPL_KEY_ORDER_COLUMNS, new String[] { Sql.KEY_RAND_ORACLE });
			break;
		default:
			throw new RuntimeException("暂不支持数据库类型[" + dbType() + "]");
		}
		conf.set(SQL_TPL_KEY_ORDER_TYPES, new String[] { Sql.KEY_ASC_TRIMED });
		return dbTemplate(SQL_TPL_COMMON_FIRSTRAND, conf).findFirst();
	}

	/**
	 * 根据条件删除数据
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	public Ret deleteBy(Okv paras, boolean customCompare) {
		if (JBoltConfig.DEMO_MODE) {
			return fail(JBoltMsg.DEMO_MODE_CAN_NOT_DELETE);
		}
		dbTemplate("common.delete",
				Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras)).delete();
		return SUCCESS;
	}

	/**
	 * 根据条件删除数据
	 *
	 * @param paras
	 * @return
	 */
	public Ret deleteBy(Okv paras) {
		return deleteBy(paras, false);
	}

	/**
	 * 切换Boolean类型字段
	 *
	 * @param id      需要切换的数据ID
	 * @param columns 需要切换的字段列表
	 * @return
	 */
	public Ret toggleBoolean(Object id, String... columns) {
		return toggleBoolean(null, id, columns);
	}

	/**
	 * 保存
	 *
	 * @param record
	 * @return
	 */
	public Ret save(Record record) {
		return save(record, primaryKey());
	}

	/**
	 * 保存
	 *
	 * @param record
	 * @param primaryKeyName
	 * @return
	 */
	public Ret save(Record record, String primaryKeyName) {
		if (record == null || notOk(primaryKeyName)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		Object idValue = record.getObject(primaryKeyName);
		String idGenMode = idGenMode();
		if(notOk(idGenMode)){
			throw new RuntimeException("service 未实现idGenMode方法");
		}
		// 如果不需要自己赋值 但是有值了 就报错
		if (!JBoltIDGenMode.isNeedSysAutoAssign(idGenMode) && isOk(idValue)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		//自动处理主键赋值
		autoProcessRecordPrimaryKeyValue(record, primaryKeyName, idValue, idGenMode);
		boolean success = Db.use(dataSourceConfigName()).save(table(), record);
		return ret(success);
	}
	/**
	 * 自动处理主键赋值
	 * @param record
	 * @param primaryKeyName
	 * @param idValue
	 * @param idGenMode
	 */
	private void autoProcessRecordPrimaryKeyValue(Record record, String primaryKeyName, Object idValue,
			String idGenMode) {
		// 如果需要赋值 但是没有赋值
		if (JBoltIDGenMode.isNeedSysAutoAssign(idGenMode) && notOk(idValue)) {
			// 那就自动赋值
			switch (idGenMode) {
			case JBoltIDGenMode.SNOWFLAKE:
				record.set(primaryKeyName, JBoltSnowflakeKit.me.nextId());
				break;
			case JBoltIDGenMode.SNOWFLAKE_STRING:
				record.set(primaryKeyName, JBoltSnowflakeKit.me.nextIdStr());
				break;
			case JBoltIDGenMode.UUID:
				record.set(primaryKeyName, IdUtil.fastSimpleUUID());
				break;
			case JBoltIDGenMode.SEQUENCE:
				record.set(primaryKeyName, getIdSequenceStr());
				break;
			case JBoltIDGenMode.SEQUENCE_LONG:
				record.set(primaryKeyName, getIdSequenceStr());
				break;
			}
		}
	}

	/**
	 * 保存
	 *
	 * @param record
	 * @return
	 */
	protected Ret update(Record record) {
		return update(record, primaryKey());
	}

	/**
	 * 更新
	 *
	 * @param record
	 * @return
	 */
	protected Ret update(Record record, String primaryKeyName) {
		if (record == null || notOk(primaryKeyName) || notOk(record.getObject(primaryKeyName))) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		boolean success = Db.use(dataSourceConfigName()).update(table(), record);
		return ret(success);
	}

	/**
	 * 获得id主键 自增序列配置
	 * @return
	 */
	private String getIdSequenceStr() {
		String dbType = dbType();
		String idSequence = idSequence();
		String idSequenceStr = null;
		// 如果不是空并且不是default 说明是自己指定的 直接赋值即可
		if (StrKit.notBlank(idSequence) && !JBoltConst.JBOLT_ID_SEQUENCE_DEFAULT.equals(idSequence)) {
			switch (dbType) {
			case DBType.POSTGRESQL:
				idSequenceStr = "nextval('" + idSequence + "')";
				break;
			case DBType.ORACLE:
				idSequenceStr = idSequence + ".nextval";
				break;
			case DBType.DM:
				idSequenceStr = idSequence + ".nextval";
				break;
			}
			return idSequenceStr;
		}
		// 判断是不是序列类型ID 然后按照内置规则处理
		String idGenMode = idGenMode();
		if(notOk(idGenMode)){
			throw new RuntimeException("service 未实现idGenMode方法");
		}
		if (JBoltIDGenMode.SEQUENCE.equalsIgnoreCase(idGenMode)
				|| JBoltIDGenMode.SEQUENCE_LONG.equalsIgnoreCase(idGenMode)) {
			String tableName = table();
			switch (dbType) {
			case DBType.POSTGRESQL:
				idSequenceStr = "nextval('" + tableName + "_idsq')";
				break;
			case DBType.ORACLE:
				idSequenceStr = tableName + "_idsq.nextval";
				break;
			case DBType.DM:
				idSequenceStr = tableName + "_idsq.nextval";
				break;
			}
		}
		return idSequenceStr;
	}

	/**
	 * 得到所有数据
	 * @return
	 */
	public List<Record> findAll(String orderColumn,boolean desc) {
		Sql sql = selectSql();
		if(isOk(orderColumn)){
			sql.orderBy(orderColumn,desc);
		}else{
			sql.orderByIdAscIfPgSql();
		}
		return find(sql);
	}

	/**
	 * 得到指定pid值的 所有节点 作为父节点 指定排序
	 *
	 * @param pidName
	 * @param parentPidValue
	 * @param sortColumnName
	 * @param desc
	 * @return
	 */
	public List<Record> getAllParents(String pidName, Object parentPidValue, String sortColumnName, boolean desc) {
		return getCommonList(Okv.by(pidName, parentPidValue), sortColumnName,
				desc ? Sql.KEY_DESC_TRIMED : Sql.KEY_ASC_TRIMED);
	}

	/**
	 * 得到指定pid值的 所有节点 作为父节点 默认正序
	 *
	 * @param pidName
	 * @param parentPidValue
	 * @param sortColumnName
	 * @return
	 */
	public List<Record> getAllParents(String pidName, Object parentPidValue, String sortColumnName) {
		return getAllParents(pidName, parentPidValue, sortColumnName, false);
	}

	/**
	 * 得到指定pid值的 所有节点 作为父节点
	 *
	 * @param parentPidValue
	 * @return
	 */
	public List<Record> getAllParents(Object parentPidValue) {
		return getAllParents(PID, parentPidValue, SORT_RANK);
	}

	/**
	 * 得到所有一级父节点
	 *
	 * @return
	 */
	public List<Record> getAllParents() {
		return getAllParents(0);
	}

	/**
	 * 得到指定父级的下级
	 *
	 * @return
	 */
	public List<Record> getSons(String pidName, Object pidValue, String sortColumnName) {
		return getCommonList(Okv.by(pidName, pidValue), sortColumnName);
	}

	/**
	 * 得到指定父级的下级 按照sortRank字段排序
	 *
	 * @return
	 */
	public List<Record> getSonsOrderBySortRank(Object pidValue) {
		return getSons(PID, pidValue, SORT_RANK);
	}

	/**
	 * 得到指定父级的下级 不带字段排序
	 *
	 * @return
	 */
	public List<Record> getSons(Object pidValue) {
		return getSons(PID, pidValue, null);
	}

	/**
	 * 得到所有的一级数据 按照ID 正序排序
	 *
	 * @return
	 */
	public List<Record> getAllParentsOrderById() {
		return getAllParents(PID, 0, ID);
	}

	/**
	 * 得到指定父级的下级数据 按照ID 正序排序
	 *
	 * @param pidValue
	 * @return
	 */
	public List<Record> getSonsOrderById(Object pidValue) {
		return getSons(PID, pidValue, ID);
	}
	/**
	 * 批量保存
	 * @param records
	 */
	protected int[] batchSave(List<Record> records) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		int batchSize = Math.min(records.size(), 500) ;
		return Db.use(dataSourceConfigName()).batchSave(table(), records, batchSize);
	}
	/**
	 * 批量保存
	 * @param records
	 * @param batchSize
	 */
	protected int[] batchSave(List<Record> records,int batchSize) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		return Db.use(dataSourceConfigName()).batchSave(table(), records, batchSize);
	}
	/**
	 * 批量更新
	 * @param records
	 */
	protected int[] batchUpdate(List<Record> records) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		int batchSize = Math.min(records.size(), 500) ;
		return Db.use(dataSourceConfigName()).batchUpdate(table(), records, batchSize);

	}
	/**
	 * 批量更新
	 *
	 * @param records
	 * @param batchSize
	 */
	protected int[] batchUpdate(List<Record> records, int batchSize) {
		if (notOk(records)){
			return NONE_BATCH_RESULT;
		}
		return Db.use(dataSourceConfigName()).batchUpdate(table(), records, batchSize);
	}

	/**
	 * 切换Boolean类型字段值
	 *
	 * @param kv      额外传入的参数 用于 toggleExtra里用
	 * @param id      需要切换的数据ID
	 * @param columns 需要切换的字段列表
	 * @return
	 */
	protected Ret toggleBoolean(Kv kv, Object id, String... columns) {
		if (notOk(id)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		String tableName = table();
		Record record = Db.use(dataSourceConfigName()).findById(tableName, id);
		if (record == null) {
			return fail(JBoltMsg.DATA_NOT_EXIST);
		}

		Boolean value;
		String v;
		for (String column : columns) {
			String msg = checkCanToggle(record, column, kv);
			if (StrKit.notBlank(msg)) {
				return fail(msg);
			}
			v = record.getStr(column);
			if (StrKit.isBlank(v)) {
				value = true;
			} else {
				// 使用String 判断
				int len = v.length();
				switch (len) {
				case 1:
					value = v.equals(TRUE);
					break;
				case 4:
					value = v.equalsIgnoreCase("true");
					break;
				case 5:
					value = !v.equalsIgnoreCase("false");
					break;
				default:
					value = true;
					break;
				}
			}
			record.set(column, !value);
		}

		boolean success = Db.use(dataSourceConfigName()).update(tableName, record);
		if (success) {
			// 处理完指定这个字段 还需要额外处理什么？
			for (String column : columns) {
				String msg = afterToggleBoolean(record, column, kv);
				if (StrKit.notBlank(msg)) {
					return fail(msg);
				}
			}
		}
		return success ? success(record, JBoltMsg.SUCCESS) : fail();

	}

	/**
	 * 常用的得到列表数据数量 自定义参数compare
	 *
	 * @param paras
	 * @param customCompare
	 * @return
	 */
	protected int getCount(Okv paras, boolean customCompare) {
		return dbTemplate(SQL_TPL_COMMON_COUNT,
				Okv.by(SQL_TPL_KEY_CUSTOM_COMPARE, customCompare).setIfNotNull(SQL_TPL_KEY_MYPARAS, paras)).queryInt();
	}

	/**
	 * 常用的得到列表数据数量
	 *
	 * @param paras
	 * @return
	 */
	protected int getCount(Okv paras) {
		return getCount(paras, false);
	}

	/**
	 * 得到新数据的排序Rank值 默认从1开始 不带任何查询条件
	 *
	 * @return
	 */
	protected int getNextSortRank() {
		return getNextSortRank(null, false);
	}

	/**
	 * 得到新数据的排序Rank值 从0开始 不带任何查询条件
	 *
	 * @return
	 */
	protected int getNextRankFromZero() {
		return getNextSortRank(null, true);
	}

	/**
	 * 得到新数据的排序Rank值 从0开始 带查询条件
	 *
	 * @param paras
	 * @return
	 */
	protected int getNextRankFromZero(Okv paras) {
		return getNextSortRank(paras, true);
	}

	/**
	 * 得到新数据的排序Rank值 自带简单条件查询默认从1开始
	 *
	 * @param paras
	 * @return
	 */
	protected int getNextSortRank(Okv paras) {
		return getNextSortRank(paras, false);
	}

	/**
	 * 得到新数据的排序Rank值 自带简单条件查询 可以自定义是否从零开始
	 *
	 * @param paras
	 * @param fromZero
	 * @return
	 */
	protected int getNextSortRank(Okv paras, boolean fromZero) {
		int count = getCount(paras);
		if (fromZero) {
			return count;
		}
		return count + 1;
	}

	/**
	 * 得到新数据的排序Rank值 自带简单条件查询 可以自定义是否从零开始 条件可定制版
	 *
	 * @param paras
	 * @param customCompare
	 * @param fromZero
	 * @return
	 */
	protected int getNextSortRank(Okv paras, Boolean customCompare, boolean fromZero) {
		int count = getCount(paras, customCompare);
		if (fromZero) {
			return count;
		}
		return count + 1;
	}

	/**
	 * 常用的得到列表数据数量
	 *
	 * @return
	 */
	protected int getCount() {
		return getCount(null, false);
	}

	public boolean tableExist() {
		String newTableName = table();
		return JBoltDbKit.tableExist(dataSourceConfigName(),newTableName);
	}

	/**
	 * 更新同级删除数据之后数据的排序
	 *
	 * @param sortRank
	 */
	protected void updateSortRankAfterDelete(Integer sortRank) {
		updateSortRankAfterDelete(table(), sortRank);
	}

	/**
	 * 更新同级删除数据之后数据的排序
	 *
	 * @param params
	 * @param sortRank
	 */
	protected void updateSortRankAfterDelete(Okv params, Integer sortRank) {
		updateSortRankAfterDelete(table(), params, sortRank);
	}

	/**
	 * 删除关联子数据
	 *
	 * @param pid
	 * @return
	 */
	protected Ret deleteByPid(Object pid) {
		return deleteBy(Okv.by(PID, pid));
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段 关键词为空的时候 返回空list
	 *
	 * @param keywords   关键词
	 * @param limitCount 返回个数
	 * @paraRecord matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<Record> getAutocompleteList(String keywords, Integer limitCount, String matchColumns) {
		return getAutocompleteList(keywords, limitCount, false, Sql.KEY_STAR, matchColumns, null);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段 关键词为空的时候 返回空list
	 *
	 * @param keywords   关键词
	 * @param limitCount 返回个数
	 * @paraRecord matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @paraRecord whereSql 额外条件sql
	 * @return
	 */
	public List<Record> getAutocompleteList(String keywords, Integer limitCount, String matchColumns, Sql whereSql) {
		return getAutocompleteList(keywords, limitCount, false, Sql.KEY_STAR, matchColumns, whereSql);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段
	 *
	 * @param keywords   关键词
	 * @param limitCount 返回个数
	 * @param always     当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @paraRecord matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<Record> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String matchColumns) {
		return getAutocompleteList(keywords, limitCount, always, Sql.KEY_STAR, matchColumns, null);
	}

	/**
	 * 关键词查询指定返回个数的数据 默认返回所有字段
	 *
	 * @param keywords   关键词
	 * @param limitCount 返回个数
	 * @param always     当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @paraRecord matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @paraRecord whereSql 额外条件sql
	 * @return
	 */
	public List<Record> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String matchColumns,
			Sql whereSql) {
		return getAutocompleteList(keywords, limitCount, always, Sql.KEY_STAR, matchColumns, whereSql);
	}

	/**
	 * 关键词查询指定返回个数的数据
	 *
	 * @param keywords      关键词
	 * @param limitCount    返回个数
	 * @param always        当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @param returnColumns 查询字段 可以是* 也可以是 id,name这种逗号隔开
	 * @paraRecord matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @return
	 */
	public List<Record> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String returnColumns,
			String matchColumns) {
		return getAutocompleteList(keywords, limitCount, always, returnColumns, matchColumns, null);
	}

	/**
	 * 关键词查询指定返回个数的数据
	 *
	 * @param keywords      关键词
	 * @param limitCount    返回个数
	 * @param always        当关键词空的时候 是否需要查询所有数据返回指定个数
	 * @param returnColumns 查询字段 可以是* 也可以是 id,name这种逗号隔开
	 * @paraRecord matchColumns 关键词去匹配哪些字段 可以一个 可以多个 逗号隔开
	 * @paraRecord whereSql 额外条件sql
	 * @return
	 */
	public List<Record> getAutocompleteList(String keywords, Integer limitCount, Boolean always, String returnColumns,
			String matchColumns, Sql whereSql) {
		if ((notOk(keywords) && (always == null || !always)) || notOk(matchColumns)) {
			return Collections.emptyList();
		}
		if (whereSql == null) {
			whereSql = selectSql();
		}else{
			whereSql.fromIfBlank(table());
		}
		whereSql.select(returnColumns).firstPage(limitCount);
		// 如果关键词为空 默认是返回空数据
		// 但是如果指定了关键词是空 就按照指定个数返回数据的话
		if (notOk(keywords) && always != null && always) {
			return find(whereSql.toSql());
		}

		String[] columns = JBoltArrayUtil.from(matchColumns, ",");
		if (columns.length == 0) {
			return Collections.emptyList();
		}
		int size = columns.length;
		whereSql.bracketLeft();
		keywords = keywords.trim();
		for (int i = 0; i < size; i++) {
			whereSql.like(columns[i], keywords);
			if (i < size - 1) {
				whereSql.or();
			}
		}
		whereSql.bracketRight();
		return find(whereSql.toSql());
	}

	/**
	 * 根据关键词分页查询 默认倒序
	 *
	 * @param orderColumn
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @paraRecord matchColumns
	 * @param otherParas
	 * @param columnToCamelCase
	 * @return
	 */
	public Page<Record> paginateByKeywords(String orderColumn, int pageNumber, int pageSize, String keywords,
			String matchColumns, Okv otherParas, boolean columnToCamelCase) {
		return paginateByKeywords(orderColumn, Sql.KEY_DESC_TRIMED, pageNumber, pageSize, keywords, matchColumns,
				otherParas, columnToCamelCase);
	}

	/**
	 * 根据关键词分页查询
	 *
	 * @param orderColumn
	 * @param orderType
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @paraRecord matchColumns
	 * @param otherParas
	 * @param columnToCamelCase 字段转驼峰
	 * @return
	 */
	public Page<Record> paginateByKeywords(String orderColumn, String orderType, int pageNumber, int pageSize,
			String keywords, String matchColumns, Okv otherParas, boolean columnToCamelCase) {
		return paginateByKeywords(Sql.KEY_STAR, orderColumn, orderType, pageNumber, pageSize, keywords, matchColumns,
				otherParas, columnToCamelCase);
	}

	/**
	 * 根据关键词匹配分页查询
	 *
	 * @param returnColumns
	 * @param orderColumn
	 * @param orderType
	 * @param pageNumber
	 * @param pageSize
	 * @param keywords
	 * @paraRecord matchColumns
	 * @param otherParas
	 * @param columnToCamelCase 字段名转驼峰
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public Page<Record> paginateByKeywords(String returnColumns, String orderColumn, String orderType, int pageNumber,
			int pageSize, String keywords, String matchColumns, Okv otherParas, boolean columnToCamelCase) {
		if (notOk(matchColumns)) {
			return emptyPage(pageSize);
		}
		Sql sql = selectSql().select(returnColumns).page(pageNumber, pageSize);
		Sql totalCountSql = selectSql().count();
		if (isOk(orderColumn) && isOk(orderType)) {
			sql.orderBy(orderColumn, orderType.equals(Sql.KEY_DESC_TRIMED));
		}
		if (otherParas != null && otherParas.size() > 0) {
			Set<String> keys = otherParas.keySet();
			sql.bracketLeft();
			totalCountSql.bracketLeft();
			for (String key : keys) {
				sql.eq(key, otherParas.getAs(key));
				totalCountSql.eq(key, otherParas.getAs(key));
			}
			sql.bracketRight();
			totalCountSql.bracketRight();
		}
		// 如果没有给keywords字段
		if (notOk(keywords)) {
			return processPaginate(sql, totalCountSql, columnToCamelCase);
		}
		// 如果给了Keywords
		String[] columns = JBoltArrayUtil.from(matchColumns, ",");
		if (columns.length == 0) {
			return emptyPage(pageSize);
		}
		int size = columns.length;
		sql.bracketLeft();
		totalCountSql.bracketLeft();
		keywords = keywords.trim();
		for (int i = 0; i < size; i++) {
			sql.like(columns[i], keywords);
			totalCountSql.like(columns[i], keywords);
			if (i < size - 1) {
				sql.or();
				totalCountSql.or();
			}
		}
		sql.bracketRight();
		totalCountSql.bracketRight();
		return processPaginate(sql, totalCountSql, columnToCamelCase);
	}

	private Page<Record> processPaginate(Sql sql, Sql totalCountSql, boolean columnToCamelCase) {
		if (!sql.hasPage()) {
			throw new RuntimeException("Please set the values of PageNumber and PageSize in the SQL");
		}
		totalCountSql.fromIfBlank(table());
		totalCountSql.clearOrderBy();
		totalCountSql.clearPage();
		Integer totalRow = queryInt(totalCountSql);
		if (totalRow == null || totalRow == 0) {
			return emptyPage(sql.getPageSize());
		}
		sql.fromIfBlank(table());
		List<Record> list = find(sql);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(list);
		}
		int totalPage = (totalRow / sql.getPageSize()) + (totalRow % sql.getPageSize() > 0 ? 1 : 0);
		return new Page<Record>(list, sql.getPageNumber(), sql.getPageSize(), totalPage, totalRow);
	}

	/**
	 * 快速获取sql 默认是select sql
	 *
	 * @return
	 */
	protected Sql selectSql() {
		return Sql.me(dbType()).select().from(table()).schema(schema());
	}

	/**
	 * 快速获取sql 使用jboltTableMenuFilter初始化
	 *
	 * @param jboltTableMenuFilter
	 * @param matchColumns
	 * @return
	 */
	public Sql selectSql(JBoltTableMenuFilter jboltTableMenuFilter, String... matchColumns) {
		Sql sql = selectSql();
		processJBoltTableMenuFilterSql(sql, jboltTableMenuFilter, matchColumns);
		return sql;
	}

	/**
	 * 快速获取sql 使用jboltTableMenuFilter初始化
	 *
	 * @param jboltTableMenuFilter
	 * @param mainTableAsName
	 * @param matchColumns
	 * @return
	 */
	public Sql selectSql(JBoltTableMenuFilter jboltTableMenuFilter, String mainTableAsName, String... matchColumns) {
		Sql sql = selectSql().from(table(), mainTableAsName);
		processJBoltTableMenuFilterSql(sql, jboltTableMenuFilter, matchColumns);
		return sql;
	}

	/**
	 * 快速获取update sql
	 *
	 * @return
	 */
	protected Sql updateSql() {
		return Sql.me(dbType()).update(table()).schema(schema());
	}

	/**
	 * 快速获取delete sql
	 *
	 * @return
	 */
	protected Sql deleteSql() {
		return Sql.me(dbType()).delete().from(table()).schema(schema());
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @return
	 */
	protected List<Record> find(Sql sql) {
		return find(sql, false);
	}

	/**
	 * 执行查询 第一个符合条件的
	 *
	 * @param sql
	 * @return
	 */
	protected Record findFirst(Sql sql) {
		return findFirst(sql, false);
	}

	/**
	 * 执行查询 第一个符合条件的
	 *
	 * @param sql
	 * @param columnToCamelCase
	 * @return
	 */
	protected Record findFirst(Sql sql, boolean columnToCamelCase) {
		if (sql.isPrepared()) {
			return findFirst(sql.toSql(), columnToCamelCase, sql.getWhereValues());
		}
		return findFirst(sql.toSql(), columnToCamelCase);
	}

	/**
	 * 执行查询 第一个符合条件的
	 *
	 * @param sql
	 * @return
	 */
	protected Record findFirst(String sql) {
		return findFirst(sql, false);
	}

	/**
	 * 执行查询 第一个符合条件的
	 *
	 * @param sql
	 * @param columnToCamelCase
	 * @param paras
	 * @return
	 */
	protected Record findFirst(String sql, boolean columnToCamelCase, Object... paras) {
		Record record = Db.use(dataSourceConfigName()).findFirst(sql, paras);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(record);
		}
		return record;
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @param columnToCamelCase 返回数据字段是否转为驼峰风格
	 * @return
	 */
	protected List<Record> find(Sql sql, boolean columnToCamelCase) {
		if (sql.isPrepared()) {
			return find(sql.toSql(), columnToCamelCase, sql.getWhereValues());
		}
		return find(sql.toSql(), columnToCamelCase);
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @return
	 */
	protected List<Record> find(String sql) {
		return find(sql, false);
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @param paras
	 * @return
	 */
	protected List<Record> find(String sql, Object... paras) {
		return find(sql, false, paras);
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @param columnToCamelCase
	 * @return
	 */
	protected List<Record> find(String sql, boolean columnToCamelCase) {
		List<Record> records = Db.use(dataSourceConfigName()).find(sql);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(records);
		}
		return records;
	}

	/**
	 * 执行查询
	 *
	 * @param sql
	 * @param columnToCamelCase
	 * @param paras
	 * @return
	 */
	protected List<Record> find(String sql, boolean columnToCamelCase, Object... paras) {
		List<Record> records = Db.use(dataSourceConfigName()).find(sql, paras);
		if (columnToCamelCase) {
			JBoltCamelCaseUtil.keyToCamelCase(records);
		}
		return records;
	}

	/**
	 * 转换为Model tree
	 *
	 * @param allList
	 * @param idColumn
	 * @param pidColumn
	 * @param checkIsParentFunc
	 * @return
	 */
	@Override
	public List<Record> convertToRecordTree(List<Record> allList, String idColumn, String pidColumn,
			TreeCheckIsParentNode<Record> checkIsParentFunc) {
		if (notOk(allList)) {
			return null;
		}
		List<Record> parents = new ArrayList<Record>();
		Record m;
		for (int i = 0; i < allList.size(); i++) {
			m = allList.get(i);
			if (checkIsParentFunc.isParent(m)) {
				parents.add(m);
				allList.remove(m);
				i--;
			}
		}
		if (parents.size() > 0 && allList.size() > 0) {
			processTreeNodes(allList, parents, idColumn, pidColumn);
		}
		return parents;
	}

	/**
	 * 处理tree节点
	 *
	 * @param allList
	 * @param parents
	 * @param idColumn
	 * @param pidColumn
	 */
	private void processTreeNodes(List<Record> allList, List<Record> parents, String idColumn, String pidColumn) {
		JBoltListMap<String, Record> map = new JBoltListMap<String, Record>();
		for (Record m : allList) {
			map.addItem("p_" + m.get(pidColumn), m);
		}
		for (Record p : parents) {
			processTreeSubNodes(map, p, idColumn);
		}

	}

	/**
	 * 递归处理tree子节点
	 *
	 * @param map
	 * @param m
	 * @param idColumn
	 */
	private void processTreeSubNodes(JBoltListMap<String, Record> map, Record m, String idColumn) {
		List<Record> items = map.get("p_" + m.get(idColumn));
		if (items != null && items.size() > 0) {
			for (Record item : items) {
				processTreeSubNodes(map, item, idColumn);
			}
		}
		m.set("items", items);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas     数据源
	 * @param openLevel -1全部 0 不动 >0指定层级
	 * @param orderColumn 排序字段 没有传null
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel,String orderColumn) {
		return convertJsTree(datas, selectedId, openLevel, null, orderColumn,false);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas          数据源
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param orderColumn    排序列 可空
	 * @param needOriginData 是否需要保留原始Data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel,String orderColumn,
			boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, null, orderColumn,needOriginData);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param keyColumn      多级下 key字段名字
	 * @param orderColumn    排序列 可空
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,String orderColumn) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, orderColumn,null,false);
	}

	/**
	 * 转换数据成为jstreebean 无指定root
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param keyColumn      多级下 key字段名字
	 * @param orderColumn    排序列 可空
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,String orderColumn,
			boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, orderColumn,null, needOriginData);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas         数据源
	 * @param selectedId    选中哪一个节点
	 * @param openLevel     -1全部 0 不动 >0指定层级
	 * @param keyColumn     多级下 key字段名字
	 * @param orderColumn   排序列 可空
	 * @param rootName      根节点名字
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,
			String orderColumn,String rootName) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, "name",orderColumn, rootName, false);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param keyColumn      多级下 key字段名字
	 * @param orderColumn    排序列 可空
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,
			String orderColumn,String rootName, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, "name", orderColumn,rootName, needOriginData);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param keyColumn      多级下 key字段名字
	 * @param textColumn     显示文本字段
	 * @param orderColumn    排序列 可空
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,
			String textColumn, String orderColumn,String rootName, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, textColumn, "enable", orderColumn,rootName, needOriginData);
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas          数据源
	 * @param selectedId     选中哪一个节点
	 * @param openLevel      -1全部 0 不动 >0指定层级
	 * @param keyColumn      多级下 key字段名字
	 * @param textColumn     显示文本字段
	 * @param enableColumn   启用禁用字段
	 * @param orderColumn    排序列 可空
	 * @param rootName       根节点名字
	 * @param needOriginData 是否需要保留原始Data
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,
			String textColumn, String enableColumn, String orderColumn,String rootName, boolean needOriginData) {
		return convertJsTree(datas, selectedId, openLevel, keyColumn, ID, PID, textColumn, enableColumn, orderColumn,rootName,
				needOriginData, null);
	}

	private String[] getTreeColumnKeys(Record m, String idColumn, String pidColumn) {
		List<String> keys = new ArrayList<String>();
		keys.add(m.getStr(idColumn));
		Object pid = m.getStr(pidColumn);
		if (pid != null && !pid.toString().equals("0")) {
			processTreeColumnKeys(keys, pid, idColumn, pidColumn);
		}
		return keys.toArray(new String[keys.size()]);
	}

	private void processTreeColumnKeys(List<String> keys, Object id, String idColumn, String pidColumn) {
		if (id == null || id.toString().equals("0")) {
			return;
		}
		Record m = findById(id);
		if (m == null) {
			return;
		}
		// pid有值 需要加入keys
		keys.add(0, id.toString());
		Object pid = m.getStr(pidColumn);
		if (pid != null && !pid.toString().equals("0")) {
			// pid有值 需要加入keys
			processTreeColumnKeys(keys, pid, idColumn, pidColumn);
		}
	}

	/**
	 * 转换数据成为jstreebean
	 *
	 * @param datas             数据源
	 * @param selectedId        选中哪一个节点
	 * @param openLevel         -1全部 0 不动 >0指定层级
	 * @param keyColumn         多级下 key字段名字
	 * @param idColumn          节点 标识字段
	 * @param pidColumn         节点父标识字段
	 * @param textColumn        显示文本字段
	 * @param enableColumn      启用禁用字段
	 * @param orderColumn       排序列 可空
	 * @param rootName          跟节点名字
	 * @param needOriginData    是否需要保留原始data
	 * @param checkIsParentFunc 检测是否为parent的Func
	 * @return
	 */
	public List<JsTreeBean> convertJsTree(List<Record> datas, Object selectedId, int openLevel, String keyColumn,
			String idColumn, String pidColumn, String textColumn, String enableColumn, String orderColumn, String rootName,
			boolean needOriginData, TreeCheckIsParentNode<Record> checkIsParentFunc) {
		List<JsTreeBean> treeBeans = new ArrayList<JsTreeBean>();
		boolean hasRoot = isOk(rootName);
		if (hasRoot) {
			treeBeans.add(new JsTreeBean(0, 0, rootName, true, "root_opened", true));
		}
		if (datas!=null&&datas.size() > 0) {
			String[] keys = null;
			if (isOk(selectedId)) {
				Record m = findById(selectedId);
				if (m != null) {
					if (isOk(keyColumn)) {
						String key = m.get(keyColumn);
						if (isOk(key)) {
							keys = JBoltArrayUtil.from(key, "_");
						}
					} else {
						keys = getTreeColumnKeys(m, idColumn, pidColumn);
					}
				}
			}
			List<Record> jstreeMs;
			if (checkIsParentFunc != null) {
				jstreeMs = convertToRecordTree(datas, idColumn, pidColumn, (p) -> checkIsParentFunc.isParent(p));
			} else {
				jstreeMs = convertToRecordTree(datas, idColumn, pidColumn, (p) -> {
					Object v = p.get(pidColumn);
					return v == null || v.toString().equals("0");
				});
			}
			processRecordJsTreeType(jstreeMs);

			processRecordJsTree(treeBeans, jstreeMs, openLevel, 1, idColumn, pidColumn, textColumn, enableColumn, orderColumn,keys,
					selectedId, hasRoot, needOriginData);
		}
		return treeBeans;
	}

	/**
	 * 查询基于menuFilter
	 *
	 * @param jboltTableMenuFilter
	 * @param columnToCamelCase
	 * @param matchColumns
	 * @return
	 */
	public Page<Record> paginateByJboltTableMenuFilter(JBoltTableMenuFilter jboltTableMenuFilter,
			boolean columnToCamelCase, String[] matchColumns) {
		return paginate(selectSql(jboltTableMenuFilter, matchColumns), columnToCamelCase);
	}

	/**
	 * 查询基于menuFilter
	 *
	 * @param jboltTableMenuFilter
	 * @param columnToCamelCase
	 * @param matchColumns
	 * @return
	 */
	public Page<Record> paginateByJboltTableMenuFilter2(JBoltTableMenuFilter jboltTableMenuFilter,
			boolean columnToCamelCase, String[] matchColumns) {
		Sql sql = selectSql(jboltTableMenuFilter, "this", matchColumns);
		sql.leftJoin("table2", "t2", "t2.id = this.t2_id");
		return paginate(sql, columnToCamelCase);
	}



	/**
	 * 根据ID 批量恢复
	 *
	 * @param ids
	 * @param split
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverByIds(String ids,String split, boolean checkCanRecover) {
		return recoverByIds(JBoltArrayUtil.from3(ids,split),checkCanRecover);
	}
	/**
	 * 根据ID 批量恢复
	 *
	 * @param ids
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverByIds(String ids, boolean checkCanRecover) {
		return recoverByIds(ids,KEY_COMMA,checkCanRecover);
	}

	/**
	 * 根据ID 批量恢复 默认不检测是否可以恢复
	 *
	 * @param ids
	 * @return
	 */
	public Ret recoverByIds(String ids) {
		return recoverByIds(ids,KEY_COMMA,false);
	}
	/**
	 * 根据ID 批量恢复
	 *
	 * @param ids
	 * @param checkCanRecover
	 * @return
	 */
	public Ret recoverByIds(Object[] ids, boolean checkCanRecover) {
		if (notOk(ids)) {
			return fail(JBoltMsg.PARAM_ERROR);
		}
		Ret ret;
		for (Object id : ids) {
			ret = recoverById(id, checkCanRecover);
			if (ret.isFail()) {
				return ret;
			}
		}
		return SUCCESS;
	}

}
